#ifndef AVALON_CHANNEL_HPP
#define AVALON_CHANNEL_HPP
#include <boost/function.hpp>
#include "Avalon/Base/Handle.hpp"
#include "Avalon/IO/IO.hpp"

namespace Avalon {
namespace IO {

  /*! \class Channel
   *  \brief Composes a Connection, a Reader, and a Writer into an IO channel.
   */
  class Channel {
    public:

      virtual ~Channel();

      //! Returns the Connection.
      virtual Connection& GetConnection() = 0;

      //! Returns the Reader.
      virtual Reader& GetReader() = 0;

      //! Returns the Writer.
      virtual Writer& GetWriter() = 0;
  };

  /*! \class BasicChannel
   *  \brief Basic implementation of the Channel interface.
   */
  class BasicChannel : public Channel {
    public:

      //! Constructs a BasicChannel.
      /*!
        \param connection The Channel's Connection.
        \param reader The Channel's Reader.
        \param writer The Channel's Writer.
      */
      BasicChannel(Carry<Connection> connection, Carry<Reader> reader,
        Carry<Writer> writer);

      //! Constructs a BasicChannel.
      /*!
        \param channel The Channel to wrap.
        \param connection The Channel's Connection.
        \param reader The Channel's Reader.
        \param writer The Channel's Writer.
      */
      BasicChannel(Carry<Channel> channel, Carry<Connection> connection,
        Carry<Reader> reader, Carry<Writer> writer);

      virtual ~BasicChannel();

      virtual Connection& GetConnection();

      virtual Reader& GetReader();

      virtual Writer& GetWriter();

    private:
      Handle<Channel> m_channel;
      Handle<Connection> m_connection;
      Handle<Reader> m_reader;
      Handle<Writer> m_writer;
  };

  /*! \class ChannelFactory
   *  \brief Factory for wrapping existing Channel components.
   */
  class ChannelFactory {
    public:

      virtual ~ChannelFactory();

      //! Creates a new Channel wrapping existing Channel components.
      /*!
        \param connection The Channel's Connection.
        \param reader The Channel's Reader.
        \param writer The Channel's Writer.
        \return A newly created Channel wrapping the specified components.
      */
      virtual Channel* Create(Carry<Connection> connection,
        Carry<Reader> reader, Carry<Writer> writer);

      //! Creates a new Channel wrapping an existing Channel.
      /*!
        \param channel The Channel to wrap.
        \return A newly created Channel wrapping the existing <i>channel</i>.
      */
      virtual Channel* Create(Channel* channel);
  };

  /*! \class WrapperChannelFactory
   *  \brief A ChannelFactory that wraps a creator function.
   */
  class WrapperChannelFactory : public ChannelFactory {
    public:

      template<typename T>
      static T* WrapChannel(Channel* source) {
        return new T(source);
      }

      template<typename T>
      static T* WrapReader(Carry<Reader> source) {
        return new T(source);
      }

      template<typename T>
      static T* WrapWriter(Carry<Writer> source) {
        return new T(source);
      }

      //! Defines the function type used to create a Channel from another.
      typedef boost::function<Channel* (Channel*)> ChannelCreator;

      //! Defines the function type used to create a Channel from components.
      typedef boost::function<Channel* (Carry<Connection>, Carry<Reader>,
        Carry<Writer>)> ComponentCreator;

      //! Defines the function type used to wrap a Reader.
      typedef boost::function<Reader* (Carry<Reader>)> ReadWrapper;

      //! Defines the function type used to wrap a Writer.
      typedef boost::function<Writer* (Carry<Writer>)> WriteWrapper;

      //! Constructs a WrapperChannelFactory.
      /*!
        \param channelCreator The function to invoke to create a Channel from
                              another channel.
      */
      WrapperChannelFactory(ChannelCreator channelCreator);

      //! Constructs a WrapperChannelFactory.
      /*!
        \param componentCreator The function to invoke to create a Channel from
                                IO components.
      */
      WrapperChannelFactory(ComponentCreator componentCreator);

      //! Creates a WrapperChannelFactory.
      /*!
        \param readWrapper The wrapper to use on the Reader.
        \param writeWrapper The wrapper to use on the Writer.
      */
      WrapperChannelFactory(ReadWrapper readWrapper, WriteWrapper writeWrapper);

      virtual ~WrapperChannelFactory();

      virtual Channel* Create(Carry<Connection> connection,
        Carry<Reader> reader, Carry<Writer> writer);

      virtual Channel* Create(Channel* channel);

    private:
      ChannelCreator m_channelCreator;
      bool m_hasChannelCreator;
      ComponentCreator m_componentCreator;
      bool m_hasComponentCreator;
  };
}
}

#endif // AVALON_CHANNEL_HPP
